/*
/*  Feuerwehrstoppuhr:
/*  @author: Markus Rechberger
/*  @description:
/*  Feuerwehrstoppuhr zum stoppen mit Bumper bzw. Lichtschranken. (bis zu 4 Möglich)
/*  Es gibt 2 Modi:
/*    * Modus 1: egal welcher Lichtschranke/Bumper schaltet -> Zeit wird gestartet
/*               egal welcher Lichtschranke/Bumper schaltet -> Zeit wird wieder gestoppt
/*               
/*    * Modus 2: mit diesem Modus werden Zwischenzeiten gestoppt
/*               wenn Zeit nicht läuft:
/*                 * egal welcher Lichtschranke/Bumper schaltet -> Zeit wird gestartet
/*               wenn Zeit läuft:
/*                 * bei Lichtschranke/Bumper die noch nicht geschaltet haben wird Zwischenzeit gestoppt
/*                 * bei Lichtschranke/Bumper die schon geschaltet haben passiert nichts.
 */

// include the library code:
#include <LiquidCrystal.h>

/* @class: Button
/* @autor: Markus Rechberger
/* @description: basic class for buttons which is LOW active
/*               the button can be pressed -> when button is pressed and isn't before
/*               the button can be pushed  -> when button is pressed and was pressed already or not doesn't matter
/*
*/
class Button //button for LOW active defined (pullup resistor is turned on)
{
  public:
    //constructor
    inline Button(int pin)
    {
      m_pin = pin;
      m_old_state = HIGH;
      m_lastPressed_ms = 0;
    }
    //destructor
    inline ~Button(){}
    inline void Update(void)
    {
      int state = digitalRead(m_pin);
      if(state == LOW)
      {
        m_isPushed = true;
        if(m_old_state == HIGH)
        {
          m_isPressed = true;
        }
        else
        {
          m_isPressed = false;
          
        }
        m_old_state = state;
      }
      else
      {
        m_isPushed = false;
        m_isPressed = false;
        m_old_state = state;
      }

    }//end Update
    inline int IsPressed(void)
    {
      return m_isPressed;
    }//end IsPressed
    
    inline int IsPushed(void)
    {
      return m_isPushed;
    }//end IsPushed
    inline int IsSecurePressed(void)
    {
      int ret = false;
      if(true == m_isPressed)
      {    
	if ((millis() - m_lastPressed_ms) > 500)
	{
	  ret = true;
	}
	else
	{
	  ret = false;
	}
	m_lastPressed_ms = millis();
      }
      else
      {
        ret = false;
      }
      return ret;
    }//end IsSecurePressed
  protected: 
    int m_pin;
    int m_old_state;  
    int m_isPressed;
    int m_isPushed;
    unsigned long m_lastPressed_ms;
};//Button

/* @class: Stopwatch
/* @author: Markus Rechberger
/* @description: Simple stopwatch, stopwatch can be started, stopped, reseted, restarted
*/
class Stopwatch
{
  public:
    //default constructor
    inline Stopwatch(void)
    {
      m_startTime = millis();
      m_stopTime = 0;
    }
    //destructor
    inline ~Stopwatch(void){}
    
    inline int IsRunning(void)
    {
      return m_isRunning;
    }//end IsRunning
    
    inline void Start(void)
    { 
      m_startTime = millis();
      m_isRunning = true;
    }//end Start
    
    inline void Stop(void)
    {
      m_stopTime = millis() - m_startTime;
      m_isRunning = false;
    }//end Stop
    
    inline void Reset(void)
    {
      if(IsRunning()) Start();
      m_stopTime = 0;
    }//end Reset
    
    inline void Restart(void)
    {
      Stop();
      Start();
    }//end Restart
    
    inline unsigned long GetTime(void)
    {
      if(true == IsRunning())
      {
        return (millis() - m_startTime);
      }
      else
      {
        return m_stopTime;
      }
    }//end GetTime
    
  protected:
    int m_isRunning;
    unsigned long m_startTime;
    unsigned long m_stopTime;
};//Stopwatch

//simple output function for LCD display
void printTimeLCD(int column, int row);
void startMenu(void);
void mode1(void);
void mode2(void);

// initialize the library with the numbers of the interface pins
//  The circuit:
// * LCD RS pin to digital pin 5
// * LCD Enable pin to digital pin 4
// * LCD D4 pin to digital pin 3
// * LCD D5 pin to digital pin 2
// * LCD D6 pin to digital pin 1
// * LCD D7 pin to digital pin 0
// * LCD R/W pin to ground
// * LCD VSS pin to ground
// * LCD VCC pin to 5V
// * 10K resistor:
// * ends to +5V and ground
// * wiper to LCD VO pin (pin 3)
LiquidCrystal lcd(5, 4, 3, 2, 1, 0); 

int stateProgramm = 0;
int oldStateProgramm = 0;
unsigned long stateProgramm_time_cnt = 0;
int selectedMode = 1;

Button buttonSelect(6);    //Pin 6
Button buttonSubmit(7);  //Pin 7

Button bumper1(9);   //Pin 9
Button bumper2(10);   //Pin 10
Button bumper3(11);   //Pin 11
Button bumper4(12);   //Pin 12

Stopwatch stopwatch;
int splittime_cnt = 0;
int bumperUsed[4];

void setup() 
{
  // set up the LCD's number of columns and rows: 
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("Modus waehlen: ");
  pinMode(6, INPUT); //set pin 6 as input
  pinMode(7, INPUT); //set pin 7 as input
  pinMode(8, INPUT); //set pin 8 as input
  pinMode(9, INPUT); //set pin 9 as input
  pinMode(10, INPUT); //set pin as input
  pinMode(11, INPUT); //set pin as input
  pinMode(12, INPUT); //set pin as input
  pinMode(13, OUTPUT); //set pin output debug LED
  digitalWrite(6, HIGH); // turn on pullup resistors. Wire button so that press shorts pin to ground
  digitalWrite(7, HIGH); // turn on pullup resistors. Wire button so that press shorts pin to ground
  digitalWrite(8, HIGH); // turn on pullup resistors. Wire button so that press shorts pin to ground
  digitalWrite(9, HIGH); // turn on pullup resistors. Wire button so that press shorts pin to ground
  digitalWrite(10, HIGH); // turn on pullup resistors. Wire button so that press shorts pin to ground
  digitalWrite(11, HIGH); // turn on pullup resistors. Wire button so that press shorts pin to ground
  digitalWrite(12, HIGH); // turn on pullup resistors. Wire button so that press shorts pin to ground
  digitalWrite(13, HIGH); //debug LED 
  
  for(int i=0;i<4;i++) bumperUsed[i] = false;
}

void loop() 
{
  //Update all buttons
  buttonSelect.Update();
  buttonSubmit.Update();
  bumper1.Update();
  bumper2.Update();
  bumper3.Update();
  bumper4.Update();
  
  //debug LED
  int value = LOW;
  if(buttonSelect.IsPressed() == true) value = HIGH;
  if(buttonSubmit.IsPressed() == true) value = HIGH;
  if(bumper1.IsPressed() == true) value = HIGH;
  if(bumper2.IsPressed() == true) value = HIGH;
  if(bumper3.IsPressed() == true) value = HIGH;
  if(bumper4.IsPressed() == true) value = HIGH;
  digitalWrite(13, value); //debug LED on Pin 13
  
  if(stateProgramm != oldStateProgramm)
  {
    stateProgramm_time_cnt = 0;
    oldStateProgramm = stateProgramm;
    lcd.clear();
  }
  
  switch(stateProgramm)
  {
    //Start Menu
    case 0: 
      startMenu();
      break;
      
    //Mode 1: 1 Bumper/Lightsensor
    case 1:
      if(stateProgramm_time_cnt == 0)
      {
        lcd.setCursor(0, 0);
        lcd.print("Modus 1");
      }
      mode1();
      break;
      
    //Mode 2: Multiple Bumper/Lightsensor Init
    case 2:
      if(stateProgramm_time_cnt == 0)
      {
        splittime_cnt = 0;
        for(int i=0;i<4;i++) bumperUsed[i] = 0;
      }
      //mode2Init();
      mode2();
      break;
      
    default:
      break;
  }
  stateProgramm_time_cnt++;
}
void printTimeLCD(unsigned long time, int column, int row)
{
  lcd.setCursor(column, row);
  lcd.print(time / 1000); 
  lcd.print(",");
  time = (time %1000)/10;
  if(time < 10) lcd.print("0");
  lcd.print(time);
}

void startMenu(void)
{
  if(true == buttonSelect.IsPressed()) selectedMode++;
  
  if(selectedMode > 2) selectedMode = 1;
  if(selectedMode < 1) selectedMode = 2;
    
  lcd.setCursor(15, 0);
  lcd.print(selectedMode);
  
  lcd.setCursor(0,1);
  switch(selectedMode)
  {
    case 1: lcd.print("1 Lichtschranke "); break;
    case 2: lcd.print("x Lichtschranken"); break;
    default: break;
  }
  
  if(true == buttonSubmit.IsPressed()) stateProgramm = selectedMode;
}//end startMenu

//in this mode the stopwatch can be started/stopped by all bumper and with buttonSelect
//the time is reseted with buttonSubmit
void mode1(void)
{
  int start_stop = false;
  int reset = false;
  if(buttonSelect.IsPressed() == true)    start_stop = true;
  if(bumper1.IsSecurePressed() == true)     start_stop = true;
  if(bumper2.IsSecurePressed() == true)     start_stop = true;
  if(bumper3.IsSecurePressed() == true)     start_stop = true;
  if(bumper4.IsSecurePressed() == true)     start_stop = true;
  if(buttonSubmit.IsPressed() == true) reset = true;
  
  if(true == start_stop)
  {
    if(stopwatch.IsRunning() == false) 
    {
      lcd.setCursor(0,1);
      lcd.print("               "); //clear display line 1
      stopwatch.Start();
    }
    else                               
    {
      stopwatch.Stop();
    }
  }
  
  if(true == reset)
  {
    stopwatch.Stop();
    stopwatch.Reset();
    oldStateProgramm = -1; //not fine, but this reset the program state
  }
  printTimeLCD(stopwatch.GetTime(),0,1);//print stopwatch time on line 1 column 0
}//end mode1

void mode2(void)
{
  //when stopwatch isn't started
  if(!stopwatch.IsRunning())
  {
    //check if any bumper is pressed, save this and start stopwatch
    if(bumper1.IsPressed() == true)
    {
      stopwatch.Start();
      bumperUsed[0] = true;
    }
    if(bumper2.IsPressed() == true)
    {
      stopwatch.Start();
      bumperUsed[1] = true;
    }    
    if(bumper3.IsPressed() == true)
    {
      stopwatch.Start();
      bumperUsed[2] = true;
    }
    if(bumper4.IsPressed() == true)
    {
      stopwatch.Start();
      bumperUsed[3] = true;
    }  
  }
  else
  {
    if(bumper1.IsPressed() == true)
    {
      if(bumperUsed[0] == false)
      {
        bumperUsed[0] = true;
        splittime_cnt++;
      }
    }    
    if(bumper2.IsPressed() == true)
    {
      if(bumperUsed[1] == false)
      {
        bumperUsed[1] = true;
        splittime_cnt++;
      }
    }   
    if(bumper3.IsPressed() == true)
    {
      if(bumperUsed[2] == false)
      {
        bumperUsed[2] = true;
        splittime_cnt++;
      }
    }
    if(bumper4.IsPressed() == true)
    {
      if(bumperUsed[3] == false)
      {
        bumperUsed[3] = true;
        splittime_cnt++;
      }
    } 
  }
  
  switch(splittime_cnt)
  {
    case 0: printTimeLCD(stopwatch.GetTime(),0,0); break;
    case 1: printTimeLCD(stopwatch.GetTime(),8,0); break;
    case 2: printTimeLCD(stopwatch.GetTime(),0,1); break;
    default: printTimeLCD(stopwatch.GetTime(),8,1); break;
  }
  
  int reset = false;
  if(buttonSubmit.IsPressed() == true) reset = true;  
  if(true == reset)
  {
    stopwatch.Stop();
    stopwatch.Reset();
    oldStateProgramm = -1; //not fine, but this reset the program state
  }
  
}//end mode2(void)
